<?php
/**
 * A rail_sequence is a sorted set of rail objects. This class provides methods like a linked list in java.
 *
 * PHP versions 5
 *
 * Copyright (C)
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of ERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software
 * Fundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
 *
 * @author Bastian Schröder <bs@chemanet.de>
 * @version 2007-01-24
 * @package rail
 */
class rail_sequence extends rail_object {

	/**
	 * The sequence pointer. Set to current rail_secence recordset.
	 *
	 * @var array
	 */
	protected $pointer = null;


	/**
	 * Remove all rail objects from sequence and set the sequence pointer to null.
	 *
	 * @exception rail_writepermission_exception
	 */
	public function clear () {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		// remove all MySQL records
		$this->rail_connector->send_request( "DELETE FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "'" );
		$this->pointer = null;
	}


	/**
	 * Delete this rail_sequence. Clear sequence before deleting object.
	 *
	 * @exception rail_writepermission_exception
	 */
	public function delete () {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		// remove content
		$this->clear();

		return parent::delete();
	}


	/**
	 * Get the current pointed rail object in this sequence. Return false if no object pointed.
	 *
	 * @exception rail_readpermission_exception
	 * @return rail_object|bool
	 */
	public function get_current () {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		// return false if pointer isn't set.
		if( $this->pointer == null || $this->pointer[ 'id' ] == 'deleted' )
			return false;

		return rail_factory::get_object( $this->rail_connector, $this->pointer[ 'object' ] );
	}


	/**
	 * Get the first rail object in this sequence.
	 *
	 * @exception rail_readpermission_exception
	 * @return rail_object
	 */
	public function get_first () {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		return $this->set_pointer( $this->rail_connector->send_request( "
			SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "' AND `previous`='0' LIMIT 1
		" ) );
	}


	/**
	 * Get the last rail object in this sequence.
	 *
	 * @exception rail_readpermission_exception
	 * @return rail_object
	 */
	public function get_last () {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		return $this->set_pointer( $this->rail_connector->send_request( "
			SELECT * FROM objects_sequences WHERE `sequence`='" .$this->get_id() . "' AND `next`='0' LIMIT 1
		" ) );
	}


	/**
	 * Get next rail object in this sequence. Returned false if sequence pointer is in end of sequence.
	 *
	 * @exception rail_readpermission_exception
	 * @return rail_object|bool
	 */
	public function get_next () {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		// return get_first if pointer isn't set
		if( $this->pointer == null ) return $this->get_first();
		// return false if pointer is in end of sequence
		else if( $this->pointer[ 'next' ] == 0 ) return false;

		// return next object in sequence
		return $this->set_pointer( $this->rail_connector->send_request( "
			SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "' AND `id`='" . $this->pointer[ 'next' ] . "' LIMIT 1
		" ) );
	}


	/**
	 * Get previous rail object in this sequence. Returned false if sequence pointer is in front of sequence.
	 *
	 * @exception rail_readpermission_exception
	 * @return rail_object|bool
	 */
	public function get_previous () {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		// return get_first if pointer isn't set
		if( $this->pointer == null ) return $this->get_first();
		// return false if pointer is in front of sequence
		else if( $this->pointer[ 'previous' ] == 0 ) return false;

		// return previous object in sequence
		return $this->set_pointer( $this->rail_connector->send_request( "
			SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "' AND `id`='" . $this->pointer[ 'previous' ] . "' LIMIT 1
		" ) );
	}


	/**
	 * Get sequence as array.
	 *
	 * @exception rail_readpermission_exception
	 * @return array
	 */
	public function get_sequence ( $pRailTypes = "", $pRecursive = false  ) {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		$data = $this->rail_connector->send_request( "SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "'" );

		$objects= array();
		while( $data_row = $data->fetch_assoc() )
			$objects[] = rail_factory::get_object( $this->rail_connector, $data_row[ 'object' ] );

		return $objects;
	}


	/**
	 * Insert rail_object(s) after current object in sequence.
	 *
	 * @exception rail_writepermission_exception
	 * @param rail_object|array $pRailObjects
	 */
	public function insert ( $pRailObjects ) {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		$objects = is_array($pRailObjects) ? $pRailObjects : array( $pRailObjects );

		// insert all objects
		$previous = $this->pointer[ 'id' ];
		foreach( $objects as $object )
			$previous = $this->insert_into( $object, $previous, $this->pointer[ 'next' ] );
	}


	/**
	 * Insert rail_object into sequnce. Return the id of new sequence item.
	 *
	 * @param rail_object $pRailObject
	 * @param int $pPrevious (optional)
	 * @param int $pNext (optional)
	 * @return int
	 */
	private function insert_into( $pRailObject, $pPrevious = 0, $pNext = 0 ){
		$id = $this->rail_connector->send_request( "
				INSERT INTO
					objects_sequences (`sequence`,`object`,`previous`,`next`)
				VALUES
					('" . $this->get_id() . "','" . $pRailObject->get_id() . "','$pPrevious','$pNext')
		" );

		// update previous element in sequence
		if( $pPrevious != 0 )
			$this->rail_connector->send_request( "UPDATE objects_sequences SET `next`='$id' WHERE `id`='$pPrevious' LIMIT 1" );

		// update next element in sequence
		if( $pNext != 0 )
			$this->rail_connector->send_request( "UPDATE objects_sequences SET `previous`='$id' WHERE `id`='$pNext' LIMIT 1" );

		return $id;
	}


	/**
	 * Remove the last object in sequence.
	 *
	 * @exception rail_writepermission_exception
	 * @return rail_object
	 */
	public function pop () {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		$object = $this->get_last();
		$this->remove_current();

		return $object;
	}


	/**
	 * Insert rail_object(s) in end of sequence.
	 *
	 * @exception rail_writepermission_exception
	 * @param array|rail_object $pRailObjects
	 */
	public function push ( $pRailObjects ) {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		$objects = is_array($pRailObjects) ? $pRailObjects : array( $pRailObjects );

		// finger out previous
		$data = $this->rail_connector->send_request( "SELECT id FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "' AND `next`='0' LIMIT 1" )->fetch_assoc();
		$previous = isset( $data[ 'id' ] ) ? $data[ 'id' ] : 0;

		// insert all objects
		foreach( $objects as $object )
			$previous = $this->insert_into( $object, $previous, 0 );
	}


	/**
	 * Remove current object from sequence.
	 *
	 * @exception rail_writepermission_exception
	 */
	public function remove_current () {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		// return if item is already deleted
		if( $this->pointer[ 'id' ] == 'deleted' )
			return;

		$this->remove_item( $this->pointer[ 'id' ] );
	}


	/**
	 * Remove one object from sequence.
	 *
	 * @param int $pId
	 */
	private function remove_item ( $pId ) {
		// get current record
		$data = $this->rail_connector->send_request( "SELECT * FROM objects_sequences WHERE `id`='" . $pId . "' LIMIT 1" )->fetch_assoc();

		// delete record
		$this->rail_connector->send_request( "DELETE FROM objects_sequences WHERE `id`='" . $pId . "'" );

		// update previous element in sequence
		if( $data[ 'previous' ] != 0 )
			$this->rail_connector->send_request( "UPDATE objects_sequences SET `next`='" . $data[ 'next' ] . "' WHERE `id`='" . $data[ 'previous' ] . "' LIMIT 1" );

		// update next element in sequence
		if( $data[ 'next' ] != 0 )
			$this->rail_connector->send_request( "UPDATE objects_sequences SET `previous`='" . $data[ 'previous' ] . "' WHERE `id`='" . $data[ 'next' ] . "' LIMIT 1" );

		// set pointer to null if current item is deleted
		if(  $this->pointer != null && $this->pointer[ 'id' ] == $pId  )
			$this->pointer[ 'id' ] = 'deleted';
	}


	/**
	 * Set pointer.
	 *
	 * @param mysqli_result $pData
	 */
	private function set_pointer( $pData ){
		// fetch assoc
		$pointer = $pData->fetch_assoc();

		// return if pointer is empty
		if( count( $pointer ) <= 0 )
			return false;

		// set pointer
		$this->pointer = array(
			'id' => $pointer[ 'id' ],
			'sequence' => $this->get_id(),
		 	'object' => $pointer[ 'object' ],
		 	'previous' => $pointer[ 'previous' ],
		 	'next' => $pointer[ 'next' ],
		);

		// return current pointed object
		return $this->get_current();
	}


	/**
	 * Set an array of rail_objects as sequence.
	 *
	 * @exception rail_writepermission_exception
	 * @param array $pRailObjects
	 */
	public function set_sequence ( $pRailObjects ) {
		// check write permission
		 if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this );

		$this->clear();
		$this->push( $pRailObjects );

		$this->pointer = null;
	}

} // End of rail_sequence
?>